Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Cookbook / Part - Appendixes
Appendix A - OpenDoc Utilities


Resource Handling (UseRsrcM)

This section describes the utilities defined in the files UseRsrcM.h and UseRsrcM.cpp. These utilities enable you to access resources from your part editor's resource fork.

Using Mac OS resources from an OpenDoc part handler is a little more difficult than from a regular Mac OS application. Part handlers are implemented as shared libraries, and the Code Fragment Manager does not automatically open the resource fork of a shared library when the library is in use. Leaving the resource fork open all the time would cause resource conflicts among libraries and their host applications, but opening it every time a library is called would have too much overhead. Instead, a code fragment is responsible for remembering where its file lives, for opening the resource fork when it needs to access resources, and for closing it when it's done.

Setting Up the Build System

To use these resource utilities, you need to add the utility file UseRsrcM.cpp to your project if you use a project-file development system, or add it to a makefile if you use MPW.

You also need to tell the build system you have a CFM initialization routine (described in the next section). In MPW, you use the -init name command line flag of the ILink or PPCLink tool. The routine can be called anything you like, but typically you append CFMInit to the part editor name, for example, SamplePartCFMInit.

Initializing Your Library

If your part editor needs to access its resources (as almost any part handler does), you need to provide a CFM initialization routine. This routine is called by the Code Fragment Manager when your library is instantiated--that is, whenever the first connection is made to your library by a process. For a part editor, this occurs the first time an instance of your part is created. The initialization routine is the very first piece of your code to be called.

The initialization routine is passed a pointer to an initialization block. This block contains an FSSpec field that gives the location on disk of the part handler library. In the implementation of your initialization routine you need to pass a pointer to the initialization block to the function InitLibraryResources so it can open the library's resource fork and keep it around for when you need to access it.

You should also provide a termination routine, which the Code Fragment Manager calls when your part editor library is unloaded. (Typically this happens when no instances of classes defined in your library are in memory and OpenDoc decides to purge memory to free up space.) The termination routine should call CloseLibraryResources to close your library's resource fork and free up the memory occupied by its resource map (and any resources from it that haven't been purged or released.)

Bare-bones initialization and termination routines look like this:

#ifndef __USERSRCM__
#include "UseRsrcM.h"#endif

#ifndef __FRAGLOAD__
#include <FragLoad.h>#endif

extern "C" pascal OSErr MyPartCFMInit( InitBlockPtr );


OSErr MyPartCFMInit (InitBlockPtr initBlkPtr)
{
    return InitLibraryResources(initBlkPtr);
}

void MyPartCFMTerminate( )
{
    CloseLibraryResources();
}
The call to InitLibraryResources opens your library's resource fork but does not put it in the resource chain. This effectively makes it invisible to the Resource Manager, but allows it to be activated at a moment's notice. The termination routine closes the resource fork and releases any memory it may have been using.

Note
The initialization routine is also a good place to do other one-time initializations, such as setting up global variables. A common thing to do is to call the Mac OS Toolbox routine Gestalt to determine whether various system services are available, and to store the results in global Boolean variables for later use. Keep in mind, though, that the initialization routine is not called every time a part is instantiated--it is called only when the library is first linked into a process.
Of course, simply declaring these routines is not enough. You need to tell the linker that these are special CFM routines. See the above section "Setting Up the Build System", as well as your development tools' documentation, for full details.

For more information on initialization and termination routines, see Inside Macintosh: PowerPC System Software.

Accessing Your Library's Resources

Before accessing your library's resources (directly or indirectly), call BeginUsingLibraryResources. This routine activates your library's resource fork and makes it the current resource file. (It also returns a magic 32-bit value that you should save for later.) You may then safely call Resource Manager routines, such as Get1Resource or Count1Resources, or Toolbox routines that indirectly call the Resource Manager, such as GetMenu or GetNewWindow.

As soon as possible, deactivate your library's resource fork by calling EndUsingLibraryResources, passing in the magic 32-bit value you received from BeginUsingLibraryResources. Leaving the resource fork active for too long can cause conflicts with other part editors (or OpenDoc subsystems) that need to use resources. In particular, you should not make any OpenDoc API calls while your resource fork is active, or (even worse) return from a call to your part without deactivating it.

Activating and deactivating your resource fork is a very quick process, without much overhead. Don't worry about it slowing down the system.

Consider the following code fragment:

ODSLong x = BeginUsingLibraryResources();
fMenu = GetMenu(kMyMenuID);
EndUsingLibraryResources(x);
fMenuBar->AddMenuLast(ev,kMyMenuID, fMenu, fPart);
After calling EndUsingLibraryResources, any resources that have been loaded into memory are still there. However, since your resource file is not active and is not in the resource chain, you can't perform any resource operations on resources in it, such as LoadResource, GetResInfo or ReleaseResource. Before you can call any Resource Manager routines on a resource you've loaded, you need to call BeginUsingLibraryResources again.

In particular, you must activate the resource file before releasing the resource, as this example shows:

ODSLong x = BeginUsingLibraryResources();
ReleaseResource((Handle)fMenu);
EndUsingLibraryResources(x);
You can't call ReleaseResource when your resource fork is inactive. And you can't just call DisposeHandle on the resource, or the Resource Manager will encounter problems.

For C++ Users

If you use C++, there is an alternative to using these calls, based on the standard C++ idiom of a lightweight stack-based class whose constructor sets up a state and whose destructor removes it. The class is called CUsingLibraryResources. Declaring an instance of CUsingLibraryResources activates your resource fork, as in this example:

{
   CUsingLibraryResources using;
   fMenu = GetMenu(kMyMenuID);
}

fMenuBar->AddMenuLast(ev,kMyMenuID, fMenu, fPart);
When the object goes out of scope (when the flow of control leaves the enclosing block) the resource fork is deactivated.

One nice aspect of the object-oriented model is that, unlike the procedural model, it lets you return or break from a block containing a CUsingLibraryResources object. The return or break statement will cause the object to go out of scope, and the compiler automatically calls the destructor.

A CUsingLibraryResources object is a Destructo (defined in the file Except.h) and so will automatically be destroyed if it goes out of scope as a result of an exception. This means that your resource fork automatically is deactivated if an exception is thrown out of the block: a very desirable thing to have happen. For this reason, if you use C++, it's preferable to use this form instead of using BeginUsingLibraryResources and EndUsingLibraryResources.

Note
Remember, the Resource Manager only loads one copy of a resource into any single process. However, any number of instances of your part may be active in a single document process. This means that, unless you explicitly use the ODReadResource utilities described in the next section, all instances of your part in a single document have to share the resources.
A common error is for a part to load a resource and then later release it, perhaps in its destructor or somUninit method. The problem is that other instances of the part might still exist in the document, and they might also have loaded the same resource. After the first part releases the resource, the other parts have invalid dangling handles and will probably end up reading garbage or corrupting the heap if they try to use the resource thereafter.

A good solution is to treat resources as global variables. Note that they have the same scope (per process) as your part handler library's global variables. This means that you can safely load a resource and assign the handle to a global variable, which can then be shared by all active instances of your part. If you release the resource, perhaps in your Purge method, set the global variable to kODNULL so that other instances of your part know it's been disposed of. They can then load the resource again the next time they read it. A more advanced variation on this is to keep a reference count on a resource and release the resource when the reference count goes to zero.

Resource-Loading Utilities

There are some resource-loading utilities you might want to use. These have the advantage that they don't load the resources into the application heap (which has very little free space in an OpenDoc environment) and that the resource data isn't shared between all instances of your part. They also take care of activating and deactivating your resource file automatically.

ODReadResource and ODReadNamedResource are comparable to GetResource and GetNamedResource, except for the following differences:

ODReadResourceToPtr and ODReadNamedResourceToPtr are similar, except that they load the resource data into a nonrelocatable block and return a pointer to it. (The block is allocated via ODNewPtr and should be disposed of via ODDisposePtr or MMFree.) These functions are obviously not appropriate for Toolbox-defined resource types like 'PICT' that have to be referenced via handles, but for your own types it can be preferable since the memory allocation is more efficient and access to the data requires only single indirection.

ODGetString reads the contents of a 'STR ' resource from your part editor into a Str255 variable that you pass in. It throws an exception (usually resNotFound) if the resource can't be read.

ODGetIndString reads a string from a 'STR#' resource from your part editor into a Str255 variable that you pass in. It is like GetIndString except that it automatically activates and deactivates your resource fork and throws an exception if the resource can't be found.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help